package gspringboot

import (
	"errors"
	"gitee.com/hongzhaomin/hzm-common-go/easyconfig"
	"gitee.com/hongzhaomin/hzm-common-go/toolkit"
	"os"
	"strconv"
	"strings"
)

func NewLocalPropertiesSource() *LocalPropertiesSource {
	return &LocalPropertiesSource{
		source: make(map[string]any, 8),
	}
}

func NewApplicationConfigPropertiesSource(cnf easyconfig.Properties) *ApplicationConfigPropertiesSource {
	return &ApplicationConfigPropertiesSource{
		Properties: cnf,
	}
}

func NewCommandLineArgsPropertiesSource(args CommandLineArgs) *CommandLineArgsPropertiesSource {
	return &CommandLineArgsPropertiesSource{
		source: args,
	}
}

func NewSystemEnvPropertiesSource() *SystemEnvPropertiesSource {
	return &SystemEnvPropertiesSource{}
}

// PropertiesSource 配置数据源接口
type PropertiesSource interface {

	// Get 根据key获取接口类型的配置值
	Get(key string) any

	// GetString 根据key获取string类型的配置值
	GetString(key string) string

	// GetInt 根据key获取int类型的配置值
	GetInt(key string) int

	// GetInt64 根据key获取int64类型的配置值
	GetInt64(key string) int64

	// GetFloat64 根据key获取float64类型的配置值
	GetFloat64(key string) float64

	// GetBool 根据key获取bool类型的配置值
	GetBool(key string) bool

	// ContainsKey 判断key是否存在
	ContainsKey(key string) bool
}

// ApplicationConfigPropertiesSource app配置数据源实现
var _ PropertiesSource = (*ApplicationConfigPropertiesSource)(nil)

type ApplicationConfigPropertiesSource struct {
	easyconfig.Properties
}

// CommandLineArgsPropertiesSource 命令行参数配置源实现
var _ PropertiesSource = (*CommandLineArgsPropertiesSource)(nil)

type CommandLineArgsPropertiesSource struct {
	source CommandLineArgs
}

func (my CommandLineArgsPropertiesSource) ContainsKey(key string) bool {
	return my.source.ContainsKey(key)
}

func (my CommandLineArgsPropertiesSource) Get(key string) any {
	return my.source.GetVal(key)
}

func (my CommandLineArgsPropertiesSource) GetString(key string) string {
	return strings.Join(my.source.GetVal(key), ",")
}

func (my CommandLineArgsPropertiesSource) GetInt(key string) int {
	vals := my.source.GetVal(key)
	if len(vals) != 1 {
		panic(errors.New("parse int error"))
	}

	val, err := strconv.Atoi(vals[0])
	if err != nil {
		panic(err)
	}
	return val
}

func (my CommandLineArgsPropertiesSource) GetInt64(key string) int64 {
	vals := my.source.GetVal(key)
	if len(vals) != 1 {
		panic(errors.New("parse int64 error"))
	}

	val, err := strconv.ParseInt(vals[0], 10, 64)
	if err != nil {
		panic(err)
	}
	return val
}

func (my CommandLineArgsPropertiesSource) GetFloat64(key string) float64 {
	vals := my.source.GetVal(key)
	if len(vals) != 1 {
		panic(errors.New("parse float64 error"))
	}

	val, err := strconv.ParseFloat(vals[0], 64)
	if err != nil {
		panic(err)
	}
	return val
}

func (my CommandLineArgsPropertiesSource) GetBool(key string) bool {
	vals := my.source.GetVal(key)
	if len(vals) != 1 {
		panic(errors.New("parse bool error"))
	}

	val, err := strconv.ParseBool(vals[0])
	if err != nil {
		panic(err)
	}
	return val
}

// SystemEnvPropertiesSource 系统环境配置源实现
var _ PropertiesSource = (*SystemEnvPropertiesSource)(nil)

type SystemEnvPropertiesSource struct{}

func (my SystemEnvPropertiesSource) ContainsKey(key string) bool {
	_, ok := os.LookupEnv(key)
	return ok
}

func (my SystemEnvPropertiesSource) Get(key string) any {
	return os.Getenv(key)
}

func (my SystemEnvPropertiesSource) GetString(key string) string {
	return os.Getenv(key)
}

func (my SystemEnvPropertiesSource) GetInt(key string) int {
	val, err := strconv.Atoi(os.Getenv(key))
	if err != nil {
		panic(err)
	}
	return val
}

func (my SystemEnvPropertiesSource) GetInt64(key string) int64 {
	val, err := strconv.ParseInt(os.Getenv(key), 10, 64)
	if err != nil {
		panic(err)
	}
	return val
}

func (my SystemEnvPropertiesSource) GetFloat64(key string) float64 {
	val, err := strconv.ParseFloat(os.Getenv(key), 64)
	if err != nil {
		panic(err)
	}
	return val
}

func (my SystemEnvPropertiesSource) GetBool(key string) bool {
	val, err := strconv.ParseBool(os.Getenv(key))
	if err != nil {
		panic(err)
	}
	return val
}

// LocalPropertiesSource 本地配置源实现
var _ PropertiesSource = (*LocalPropertiesSource)(nil)

type LocalPropertiesSource struct {
	source map[string]any
}

func (my LocalPropertiesSource) ContainsKey(key string) bool {
	_, ok := my.source[key]
	return ok
}

func (my LocalPropertiesSource) Get(key string) any {
	return my.source[key]
}

func (my LocalPropertiesSource) GetString(key string) string {
	return getValue(key, my, func() string {
		valStr, err := toolkit.ConvertStr4Any(my.source[key])
		if err != nil {
			return ""
		}
		return valStr
	})
}

func (my LocalPropertiesSource) GetInt(key string) int {
	return getValue(key, my, func() int {
		val, _ := toolkit.Str2Int(my.GetString(key))
		return val
	})
}

func (my LocalPropertiesSource) GetInt64(key string) int64 {
	return getValue(key, my, func() int64 {
		val, _ := toolkit.Str2Int64(my.GetString(key))
		return val
	})
}

func (my LocalPropertiesSource) GetFloat64(key string) float64 {
	return getValue(key, my, func() float64 {
		val, _ := toolkit.Str2Float64(my.GetString(key))
		return val
	})
}

func (my LocalPropertiesSource) GetBool(key string) bool {
	return getValue(key, my, func() bool {
		val, _ := strconv.ParseBool(my.GetString(key))
		return val
	})
}

func (my LocalPropertiesSource) set(key string, val any) {
	my.source[key] = val
}
